package ngamejava2;

public class GameLoop implements Runnable
{
	private boolean terminated;
	private long expectedNanosPerTick;
	private int expectedTPS;
	private int maxFrameSkip;
	private GameSpeedTracker speedTracker;
	private iGameLoopListener gameLoopListener;
	private Thread gameLoopThread;

	public GameLoop()
	{
		speedTracker = new GameSpeedTracker();
		setExpectedTPS(60);
		setMaxFrameSkip(10);
	}

	public void setListener(iGameLoopListener aGameLoopListener)
	{
		gameLoopListener = aGameLoopListener;
	}

	public void start()
	{
		System.out.println("Iniciando IzyEngine...");
		gameLoopThread = new Thread(this);
		gameLoopThread.start();
	}

	public void run()
	{
		try
		{
			System.out.println("GAME LOOP STARTED.");
			speedTracker.start();
			terminated = false;
			long nanoTimeAtNextTick = System.nanoTime();
			int skippedFrames = 0;
			if (gameLoopListener != null)
			{
				gameLoopListener.gameLoopLoad();
			}
			boolean updated = false;
			long sleepInternal = 10;
			while (!terminated)
			{
				speedTracker.update();
				if (System.nanoTime() > nanoTimeAtNextTick && skippedFrames	< maxFrameSkip)
				{
					nanoTimeAtNextTick += expectedNanosPerTick;
					InputManager.getInstance().update();
					if (gameLoopListener != null)
					{
						gameLoopListener.gameLoopUpdate(speedTracker.countTick());
						updated = true;
					}
					skippedFrames++;
				} else if (updated)
				{
					if (gameLoopListener != null)
					{
						gameLoopListener.gameLoopRender(speedTracker.countFrame());
						updated = false;
					}
					skippedFrames = 0;
				} else
				{
					speedTracker.countIdleLoop();
				}

				if (speedTracker.getIdleLoopsPerSecond() > 10000)
				{
					if (sleepInternal < 10)
					{
						sleepInternal++;
					}
				} else
				{
					if (sleepInternal > 1)
					{
						sleepInternal--;
					}
				}
				try
				{
					Thread.sleep(sleepInternal);
				} catch (InterruptedException e)
				{
				}
			}
			if (gameLoopListener != null)
			{
				gameLoopListener.gameLoopUnload();
			}
			System.out.println("GAME LOOP FINISHED.");
			System.out.println("\nIzyEngine desligado.\n");
		} catch (Exception e)
		{
			System.out.println("GAME LOOP ABORTED.");
			System.out.println("\n* IzyEngine desligado com erro:\n  " + e.getClass().getName() + ": " + e.getMessage() + "\n");
			throw new RuntimeException(e);
		}
	}

	public void terminate()
	{
		terminated = true;
	}

	public void setExpectedTPS(int expectedTPS)
	{
		this.expectedTPS = expectedTPS;
		expectedNanosPerTick = Math.round(GameSpeedTracker.NANOS_IN_ONE_SECOND	/ expectedTPS);
	}

	public int getExpectedTPS()
	{
		return expectedTPS;
	}

	public void setMaxFrameSkip(int maxFrameSkip)
	{
		this.maxFrameSkip = maxFrameSkip;
	}

	public int getMaxFrameSkip()
	{
		return maxFrameSkip;
	}

	public GameSpeedTracker getSpeedTracker()
	{
		return speedTracker;
	}
}